﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using System.Xml.Linq;
using Microsoft.SharePoint.WebPartPages;
using Microsoft.SharePoint.Navigation;
using SlamCms.Common;

namespace SlamCms.SharePoint
{
    public class PublishingSiteService
    {
        //public static void CopySite(string sourceSiteUrl, string destinationSiteUrl)
        //{
        //    var sourceSite = new SPSite(sourceSiteUrl);
        //    var sourceWeb = sourceSite.OpenWeb();

        //    var destinationSite = new SPSite(destinationSiteUrl);
        //    var destinationWeb = destinationSite.OpenWeb();


        //    foreach (SPList sourceList in sourceWeb.Lists)
        //    {
        //        if (destinationWeb.Lists.TryGetList(sourceList.Title) != null)
        //        {
        //            //destinationWeb.Lists.Delete(destinationWeb.Lists.TryGetList(list.Title).ID);

        //        }
        //        else
        //        {
        //            Guid destinationListId = destinationWeb.Lists.Add(sourceList.Title, sourceList.Description, sourceList.BaseTemplate);

        //            SPList destinationList = destinationWeb.Lists[destinationListId];
        //            // THe Fields structure is copied
        //            foreach (SPField sourceField in sourceList.Fields)
        //            {
        //                if (!destinationList.Fields.ContainsField(sourceField.InternalName))
        //                {
        //                    XDocument fieldXdoc = XDocument.Parse(sourceField.SchemaXml);
        //                    XElement fieldElement = fieldXdoc.Element("Field");
        //                    XAttribute displayName = fieldElement.Attribute("DisplayName");
        //                    displayName.Value = sourceField.InternalName;
        //                    string fieldName = destinationList.Fields.AddFieldAsXml(fieldXdoc.ToString());
        //                    SPField destinationfield = destinationList.Fields.GetField(fieldName);
        //                    //destinationfield.Update();
        //                    destinationfield.Title = sourceField.Title;
        //                    destinationfield.Update();
        //                }

        //            }

        //            destinationList.OnQuickLaunch = sourceList.OnQuickLaunch;
        //            destinationList.


        //            destinationList.Update();
        //        }

        //    }

        //    destinationWeb.Update();


        //}

        public static void CopySite(string sourceSiteUrl, string destinationSiteUrl)
        {

            var sourceSite = new SPSite(sourceSiteUrl);
            var sourceWeb = sourceSite.OpenWeb();

            var destinationSite = new SPSite(destinationSiteUrl);
            var destinationWeb = destinationSite.OpenWeb();

            CopyPages(sourceWeb, destinationWeb);

            CopyLists(sourceWeb, destinationWeb);

            CopyQuickLaunch(sourceWeb, destinationWeb);

            //Dispose all objects
            sourceSite.Dispose();
            sourceWeb.Dispose();
            destinationSite.Dispose();
            destinationWeb.Dispose();
        }


      

        private static void CopyLists(SPWeb sourceWeb, SPWeb destinationWeb)
        {
            List<string> templates = new List<string>();

            foreach (SPList sourceList in sourceWeb.Lists)
            {
                if ((sourceList.BaseTemplate == SPListTemplateType.PictureLibrary || sourceList.BaseTemplate == SPListTemplateType.DocumentLibrary ||
                    sourceList.BaseTemplate == SPListTemplateType.GenericList || sourceList.BaseTemplate == SPListTemplateType.Events) &&
                    !sourceList.Hidden && !sourceList.RootWebOnly)

                    if (destinationWeb.Lists.TryGetList(sourceList.Title) == null)
                    {
                        String templateName = sourceList.Title;
                        String templateFileName = sourceList.Title;
                        sourceList.SaveAsTemplate(templateFileName, templateName, sourceList.Description, ContainsPages(sourceList));
                        SPListTemplate listTemplate = sourceWeb.Site.GetCustomListTemplates(sourceWeb)[templateName];
                        // Save the template name to be deleted at the end.
                        templates.Add(listTemplate.Name);

                        destinationWeb.Lists.Add(sourceList.Title, sourceList.Description, listTemplate);
                        destinationWeb.Update();

                        // Delete the template
                        SPListItem templateItem = sourceWeb.Site.RootWeb.Lists["List Template Gallery"].Items.Cast<SPListItem>()
                                                 .First(it => it.Title.Equals(listTemplate.Name));
                        templateItem.Delete();
                    }
            }
        }


        private static bool ContainsPages(SPList list)
        {
            var items = from SPListItem item in list.Items
                        where item.Url.Contains(".aspx")
                        select item;

            return (items.Count() > 0);
        }


        private static void CopyPages(SPWeb sourceWeb, SPWeb destinationWeb)
        {
            foreach (SPFile page in sourceWeb.Files)
            {
                destinationWeb.Files.Add(page.Url, page.OpenBinaryStream(), true);

                destinationWeb.Update();

                SPFile destinationFile = destinationWeb.GetFile(page.Url);

                //var sourceWpManager = sourceWeb.GetLimitedWebPartManager(page.Url, System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared);

                var sourceWpManager = page.GetLimitedWebPartManager(System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared);
                var destinationWpManager = destinationFile.GetLimitedWebPartManager(System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared);

                //foreach (WebPart wp in sourceWpManager.WebParts)
                //{
                //    string zoneId = sourceWpManager.GetZoneID(wp);
                //    destinationWpManager.AddWebPart(wp, zoneId, wp.ZoneIndex);
                //}

                for (int i = 0; i < sourceWpManager.WebParts.Count; i++)
                {
                    bool added = false;
                    System.Web.UI.WebControls.WebParts.WebPart wp_WebUI = sourceWpManager.WebParts[i] as System.Web.UI.WebControls.WebParts.WebPart;
                    if (wp_WebUI != null)
                    {
                        string zoneId = sourceWpManager.GetZoneID(wp_WebUI);
                        destinationWpManager.AddWebPart(wp_WebUI, zoneId, wp_WebUI.ZoneIndex);
                        added = true;
                    }

                    //WebPart wp_SPS = sourceWpManager.WebParts[i] as WebPart;
                    //if (wp_SPS != null && !added)
                    //{
                    //    string zoneId = sourceWpManager.GetZoneID(wp_SPS);
                    //    destinationWpManager.AddWebPart(wp_SPS, zoneId, wp_SPS.ZoneIndex);
                    //}
                }


            }

        }



        private static void CopyQuickLaunch(SPWeb sourceWeb, SPWeb destinationWeb)
        {
            destinationWeb.AllowUnsafeUpdates = true;
            CleanQuickLaunch(destinationWeb);

            foreach (SPNavigationNode sourceNode in sourceWeb.Navigation.QuickLaunch)
            {
                string nodeUrl = sourceNode.Url.Replace(sourceWeb.ServerRelativeUrl, destinationWeb.ServerRelativeUrl);
                SPNavigationNode destinationNode = new SPNavigationNode(sourceNode.Title, nodeUrl, sourceNode.IsExternal);
                destinationWeb.Navigation.QuickLaunch.AddAsLast(destinationNode);

                CopyQuickLaunchNode(sourceNode, destinationNode);
            }
            destinationWeb.Update();

            destinationWeb.AllowUnsafeUpdates = false;
        }

        private static void CopyQuickLaunchNode(SPNavigationNode sourceNode, SPNavigationNode destionationNode)
        {
            if (sourceNode.Children.Count > 0)
                foreach (SPNavigationNode sourceChild in sourceNode.Children)
                {
                    //destionationNode.Children = new SPNavigationNodeCollection();
                    string nodeUrl = sourceChild.Url.Replace(sourceNode.Navigation.Web.ServerRelativeUrl, destionationNode.Navigation.Web.ServerRelativeUrl);
                    SPNavigationNode destinationChild = destionationNode.Children.AddAsLast(new SPNavigationNode(sourceChild.Title, nodeUrl, sourceChild.IsExternal));
                    destionationNode.Update();
                    CopyQuickLaunchNode(sourceChild, destinationChild);
                }
        }

        private static void CleanQuickLaunch(SPWeb web)
        {
            //web.AllowUnsafeUpdates = true;
            for (int i = web.Navigation.QuickLaunch.Count - 1; i > -1; i--)
            {
                web.Navigation.QuickLaunch[i].Delete();
            }
            //web.AllowUnsafeUpdates = false;

        }

        private static void CleanQuickLaunchNode(SPNavigationNode node)
        {
            if (node.Children.Count > 0)
            {
                foreach (SPNavigationNode childNode in node.Children)
                {
                    CleanQuickLaunchNode(childNode);
                    childNode.Delete();
                }
            }

        }

        #region Sync Process

        public static void SyncSite(string sourceSiteUrl, string destinationSiteUrl)
        {
            var sourceSite = new SPSite(sourceSiteUrl);
            var sourceWeb = sourceSite.OpenWeb();

            var destinationSite = new SPSite(destinationSiteUrl);
            var destinationWeb = destinationSite.OpenWeb();

            //SyncViews(sourceWeb, destinationWeb,"");
        }

        public static List<SyncTrace> SyncSite(SPWeb sourceWeb, SPWeb destinationWeb)
        {
            var processTrace = new List<SyncTrace>();
            string groupID = RandomHelpers.Generate(4, CharacterType.Uppercase);
            processTrace.Add(new SyncTrace("Synchronizing " + destinationWeb.Url, Relevance.ProcessMessage, groupID));
            processTrace.AddRange(PublishingSiteService.SyncLists(sourceWeb, destinationWeb, groupID));
            //processTrace.AddRange(PublishingSiteService.SyncWebParts(sourceWeb, destinationWeb, groupID));
            //processTrace.AddRange(PublishingSiteService.SyncViews(sourceWeb, destinationWeb, groupID));

            return processTrace;

        }

        //public static void SyncSite(string sourceSiteUrl, string sourceRelativeUrl, string destinationSiteUrl, string destinationRelativeUrl)
        //{
        //    var sourceSite = new SPSite(sourceSiteUrl);
        //    var sourceWeb = sourceRelativeUrl != null ? sourceSite.OpenWeb(sourceRelativeUrl) : sourceSite.OpenWeb();

        //    var destinationSite = new SPSite(destinationSiteUrl);
        //    var destinationWeb = destinationRelativeUrl != null ? destinationSite.OpenWeb(destinationRelativeUrl) : destinationSite.OpenWeb();

        //    SyncViews(sourceWeb, destinationWeb);
        //    //SyncPages(sourceWeb, destinationWeb);
        //}

        private static List<SyncTrace> SyncLists(SPWeb sourceWeb, SPWeb destinationWeb, string groupID)
        {
            List<SyncTrace> processTrace = new List<SyncTrace>();
            string primaryID = (!String.IsNullOrEmpty(groupID) ? (groupID + "-") : String.Empty) + RandomHelpers.Generate(4, CharacterType.Uppercase);
            processTrace.Add(new SyncTrace("Synchronizing Lists from " + destinationWeb.Name + ".", Relevance.ProcessMessage, primaryID));

            List<string> templates = new List<string>();
            foreach (SPList sourceList in sourceWeb.Lists)
            {
                if ((sourceList.BaseTemplate == SPListTemplateType.PictureLibrary || sourceList.BaseTemplate == SPListTemplateType.DocumentLibrary ||
                    sourceList.BaseTemplate == SPListTemplateType.GenericList || sourceList.BaseTemplate == SPListTemplateType.Events) &&
                    !sourceList.Hidden && !sourceList.RootWebOnly)
                {

                    string processID = primaryID + "-" + RandomHelpers.Generate(4, CharacterType.Uppercase);

                    if (destinationWeb.Lists.TryGetList(sourceList.Title) == null)
                    {
                        processTrace.Add(new SyncTrace("List " + sourceList.Title + " does not exists.", Relevance.ProcessMessage, processID));

                        String templateName = sourceList.Title;
                        String templateFileName = sourceList.Title;
                        sourceList.SaveAsTemplate(templateFileName, templateName, sourceList.Description, ContainsPages(sourceList));
                        SPListTemplate listTemplate = sourceWeb.Site.GetCustomListTemplates(sourceWeb)[templateName];
                        // Save the template name to be deleted at the end.
                        templates.Add(listTemplate.Name);
                        try
                        {
                            var addedListGUID = destinationWeb.Lists.Add(sourceList.Title, sourceList.Description, listTemplate);
                            destinationWeb.Update();

                            processTrace.Add(new SyncTrace("List " + destinationWeb.Lists[addedListGUID].Title + " created.", Relevance.ProcessMessage, processID));
                        }
                        catch (Exception ex)
                        {
                            string errorID = processID + "-" + RandomHelpers.Generate(4, CharacterType.Uppercase);
                            processTrace.Add(new SyncTrace("List " + sourceList.Title + " could not be added.", Relevance.ProcessMessage, errorID));
                            processTrace.Add(new SyncTrace("Error: " + ex.Message + " [" + ex.StackTrace + "]", Relevance.Error, errorID));
                        }



                        // Delete the template
                        SPListItem templateItem = sourceWeb.Site.RootWeb.Lists["List Template Gallery"].Items.Cast<SPListItem>()
                                                 .First(it => it.Title.Equals(listTemplate.Name));
                        templateItem.Delete();
                    }
                    else
                    {
                        processTrace.Add(new SyncTrace("List " + sourceList.Title + " already exists.", Relevance.ProcessMessage, processID));
                    }
                }
            }
            return processTrace;
        }

        public static List<SyncTrace> SyncViews(SPWeb sourceWeb, SPWeb destinationWeb, string groupID)
        {
            //StringBuilder builder = new StringBuilder("");
            List<SyncTrace> processTrace = new List<SyncTrace>();
            string primaryID = (!String.IsNullOrEmpty(groupID) ? (groupID + "-") : String.Empty) + RandomHelpers.Generate(4, CharacterType.Uppercase);
            processTrace.Add(new SyncTrace("Synchronizing Views from " + destinationWeb.Name + ".", Relevance.ProcessMessage, primaryID));
            try
            {
                foreach (SPList sourceList in sourceWeb.Lists)
                {
                    if ((sourceList.BaseTemplate == SPListTemplateType.PictureLibrary || sourceList.BaseTemplate == SPListTemplateType.DocumentLibrary ||
                        sourceList.BaseTemplate == SPListTemplateType.GenericList || sourceList.BaseTemplate == SPListTemplateType.Events) &&
                        !sourceList.Hidden && !sourceList.RootWebOnly)

                        if (destinationWeb.Lists.TryGetList(sourceList.Title) != null)
                        {
                            string listID = primaryID + "-" + RandomHelpers.Generate(4, CharacterType.Uppercase);
                            //builder.AppendLine("Synchronizing views on list: " + sourceList.Title);
                            processTrace.Add(new SyncTrace("List: " + sourceList.Title, Relevance.ProcessMessage, listID));
                            var destinationList = destinationWeb.Lists[sourceList.Title];

                            foreach (SPView sourceView in sourceList.Views)
                            {
                                //bool add = true;
                                string viewID = listID + "-" + RandomHelpers.Generate(4, CharacterType.Uppercase);
                                // if(HasView(destinationList.Views, sourceView.Title))
                                // CHeck if there are changes to apply on the view.
                                if (destinationList.Views.Cast<SPView>().Where(o => o.Title.Equals(sourceView.Title)).FirstOrDefault() != null)
                                {
                                    SPView destinationView = destinationList.Views[sourceView.Title];

                                    var destinationFields = destinationView.ViewFields.ToStringCollection().Cast<string>().ToArray();
                                    var sourceFields = sourceView.ViewFields.ToStringCollection().Cast<string>().ToArray();

                                    if (!destinationView.Query.Equals(sourceView.Query) || sourceFields.Except(destinationFields).Count() != 0)
                                    {
                                        //builder.AppendLine("View " + destinationView.Title + " is not sync.");
                                        string processID = viewID + "-" + RandomHelpers.Generate(4, CharacterType.Uppercase);
                                        processTrace.Add(new SyncTrace("View " + destinationView.Title + " is not sync.", Relevance.ProcessMessage, processID));

                                        //Update View query
                                        destinationView.Query = sourceView.Query;
                                        //Add Fields
                                        var fields = sourceFields.Except(destinationFields).ToArray();
                                        foreach (string f in fields)
                                        {
                                            destinationView.ViewFields.Add(f);
                                            processTrace.Add(new SyncTrace("Field: " + f + " added.", Relevance.ProcessMessage, processID + "-" + RandomHelpers.Generate(4, CharacterType.Uppercase)));
                                        }

                                        destinationView.Update();

                                    }
                                    //else
                                    //{
                                    //    //builder.AppendLine("View " + destinationView.Title + " is sync.");
                                    //    processTrace.Add(new SyncTrace("View " + destinationView.Title + " is sync.",Relevance.ProcessMessage,viewID+"-"+RandomHelpers.Generate(4,CharacterType.Uppercase)));
                                    //    add = false;
                                    //}
                                    processTrace.Add(new SyncTrace("View " + destinationView.Title + " is sync.", Relevance.ProcessMessage, viewID + "-" + RandomHelpers.Generate(4, CharacterType.Uppercase)));
                                }

                                // In case they are needed the view was removed and need to be added again using the source view as template.
                                //if (add)
                                else
                                {
                                    try
                                    {
                                        SPView addedView;

                                        if (sourceView.Title.Equals("All Items"))
                                        {
                                            addedView = destinationList.Views.Add("AllItems", sourceView.ViewFields.ToStringCollection(), sourceView.Query, sourceView.RowLimit, sourceView.Paged, sourceView.DefaultView);
                                            destinationList.Update();
                                            destinationList.Views["AllItems"].Title = "All Items";
                                        }
                                        else
                                        {
                                            addedView = destinationList.Views.Add(sourceView.Title, sourceView.ViewFields.ToStringCollection(), sourceView.Query, sourceView.RowLimit, sourceView.Paged, sourceView.DefaultView);

                                        }


                                        //var addedView = destinationList.Views.Add(sourceView.Title, sourceView.ViewFields.ToStringCollection(), sourceView.Query, sourceView.RowLimit, sourceView.Paged, sourceView.DefaultView);
                                        //builder.AppendLine("Changes applied");
                                        processTrace.Add(new SyncTrace("View " + addedView.Title + " added.", Relevance.ProcessMessage, viewID + "-" + RandomHelpers.Generate(4, CharacterType.Uppercase)));
                                        destinationList.Update();
                                    }
                                    catch (Exception ex)
                                    {
                                        string errorID = viewID + "-" + RandomHelpers.Generate(4, CharacterType.Uppercase);
                                        processTrace.Add(new SyncTrace("View " + sourceView.Title + " could not be synchronized.", Relevance.ProcessMessage, errorID));
                                        processTrace.Add(new SyncTrace("Error: " + ex.Message + " [" + ex.StackTrace + "]", Relevance.Error, errorID));
                                        //builder.AppendLine("View " + sourceView.Title + " could not be synchronized.");
                                        //builder.AppendLine("Error: " + ex.Message + " [" + ex.StackTrace + "]");
                                    }
                                }
                                //else
                                //{
                                //    //builder.AppendLine("No changes were applied");
                                //    processTrace.Add(new SyncTrace("No changes were applied",Relevance.ProcessMessage,viewID + "-" + RandomHelpers.Generate(4, CharacterType.Uppercase)));
                                //}
                                // builder.AppendLine("-----------------");
                            }
                        }
                }
            }
            catch (Exception ex)
            {
                //string error = builder.ToString();
                string errorID = primaryID + "-" + RandomHelpers.Generate(4, CharacterType.Uppercase);
                processTrace.Add(new SyncTrace("Synchronization was interrupted.", Relevance.ProcessMessage, errorID));
                processTrace.Add(new SyncTrace("Error: " + ex.Message + " [" + ex.StackTrace + "]", Relevance.Error, errorID));
                //builder.AppendLine("Synchronization was interrupted. Error: " + ex.Message + " [" + ex.StackTrace + "]");
            }

            //return builder.ToString();
            return processTrace;
        }


        public static List<SyncTrace> SyncWebParts(SPWeb sourceWeb, SPWeb destinationWeb, string groupID)
        {
            //SyncTrace trace = new SyncTrace();
            List<SyncTrace> processTrace = new List<SyncTrace>();
            string primaryID = (!String.IsNullOrEmpty(groupID) ? (groupID + "-") : String.Empty) + RandomHelpers.Generate(4, CharacterType.Uppercase);
            //processTrace.Add(new SyncTrace)
            processTrace.Add(new SyncTrace("Synchronizing WebParts from " + destinationWeb.Name + ".", Relevance.ProcessMessage, primaryID));
            //StringBuilder builder = new StringBuilder("");
            try
            {
                foreach (SPFile page in sourceWeb.Files)
                {
                    SPFile destinationFile = destinationWeb.GetFile(page.Url);
                    if (destinationFile.Exists)
                    {
                        string pageID = primaryID + "-" + RandomHelpers.Generate(4, CharacterType.Uppercase);
                        //builder.AppendLine("Synchronizing webparts on page: " + destinationFile.Url);
                        processTrace.Add(new SyncTrace("Page: " + destinationFile.Url, Relevance.ProcessMessage, pageID));

                        var sourceWpManager = page.GetLimitedWebPartManager(System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared);
                        var destinationWpManager = destinationFile.GetLimitedWebPartManager(System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared);

                        bool changes = false;

                        //foreach (System.Web.UI.WebControls.WebParts.WebPart wp in sourceWpManager.WebParts)
                        for (int i = 0; i < sourceWpManager.WebParts.Count; i++)
                        {
                            string wpID = pageID + "-" + RandomHelpers.Generate(4, CharacterType.Uppercase);
                            var wp = sourceWpManager.WebParts[i] as System.Web.UI.WebControls.WebParts.WebPart;
                            if (wp != null)
                            {
                                string zoneId = sourceWpManager.GetZoneID(wp);
                                string action = "";
                                if (destinationWpManager.WebParts.Cast<System.Web.UI.WebControls.WebParts.WebPart>().Where(o => o.Title.Equals(wp.Title) && destinationWpManager.GetZoneID(o).Equals(zoneId)).FirstOrDefault() == null)
                                {
                                    destinationWpManager.AddWebPart(wp, zoneId, wp.ZoneIndex);
                                    action = "was added";
                                    changes = true;
                                }
                                else
                                {
                                    action = "already exists";
                                }

                                string message = "WebPart: " + wp.Title + " " + action + ". (Zone: " + zoneId + " - Index: " + wp.ZoneIndex.ToString() + ")";
                                processTrace.Add(new SyncTrace(message, Relevance.ProcessMessage, wpID));
                            }
                            else
                            {
                                //builder.AppendLine("A webpart could not be loaded.");
                                processTrace.Add(new SyncTrace("A webpart could not be loaded.", Relevance.AdditionalInformation, wpID));
                            }
                        }

                        if (changes)
                            destinationFile.Update();
                    }
                }
            }
            catch (Exception ex)
            {
                string message = "Synchronization was interrupted. Error: " + ex.Message + " [" + ex.StackTrace + "]";
                processTrace.Add(new SyncTrace(message, Relevance.Error, primaryID + "-" + RandomHelpers.Generate(4, CharacterType.Uppercase)));
                //builder.AppendLine("Synchronization was interrupted. Error: " + ex.Message + " [" + ex.StackTrace + "]");
            }

            //return builder.ToString();
            return processTrace;
        }

        //private static bool AddWebPart()


        #endregion


    }

    public class SyncTrace
    {
        public SyncTrace(string message, Relevance relevance, string groupID)
        {
            this.Message = message;
            this.Relevance = relevance;
            this.GroupID = groupID;
            this.TimeStamp = DateTime.Now;
        }

        public SyncTrace()
        {

        }

        public string Message { get; set; }
        public Relevance Relevance { get; set; }
        public DateTime TimeStamp { get; set; }
        public string GroupID { get; set; }
    }

    public enum Relevance
    {
        ProcessMessage,
        Error,
        AdditionalInformation

    }

}
